home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
C/C++ Users Group Library 1996 July
/
C-C++ Users Group Library July 1996.iso
/
listings
/
v_09_09
/
9n09060a
< prev
next >
Wrap
Text File
|
1991-07-22
|
10KB
|
381 lines
/*
* asn1.c -- functions for decoding encoded ASN.1
*/
#include <stdlib.h>
#include <stdio.h>
#ifdef __STDC__
#include <limits.h>
#endif /* __STDC__ */
#include <ctype.h>
#include <string.h>
#include "asn1.h"
#ifndef USHRT_MAX
#define USHRT_MAX 65535
#endif
#ifndef UINT_MAX
#define UINT_MAX USHRT_MAX
#endif
#ifndef SIZE_T_MAX
#define SIZE_T_MAX UINT_MAX
#endif
#define BOOLEAN_TYPE 1
#define INTEGER_TYPE 2
#define BITSTRING_TYPE 3
#define NULL_TYPE 5
#ifndef TRUE
#define FALSE 0
#define TRUE 1
#endif
#define BIT_8 128
#define BIT_1 1
#define BITS_54321 31
#define BITS_7654321 127
#define punt(x) { printf(" -- %s\n", x); exit(1); }
#define the_class(x) (*x >> 6 & 3)
#define the_complexity(x) ((*x >> 5) & BIT_1)
static unsigned int
the_tag_number(encoding)
char **encoding;
/*
* This function returns the tag number of an ASN.1
* encoding. The argument must point to a pointer to
* the first octet of the encoding. The pointer is
* advanced to the first octet of the length of the
* encoding.
*
* BUG: Tag numbers greater than UINT_MAX have their
* high order bits stripped.
*/
{
unsigned int t;
t = ((unsigned char) **encoding) & BITS_54321;
(*encoding)++;
if (BITS_54321 == t) { /* high tag number? */
t = 0;
while (**encoding & BIT_8) {
t << = 7;
t |= **encoding & BITS_7654321;
(*encoding)++;
}
t << = 7;
t |= **encoding;
(*encoding)++;
}
return t;
}
static size_t
the_length(encoding)
char **encoding;
/*
* This function returns the length of an ASN.1
* encoding. The pointer passed in must be pointing
* to the first length octet of the encoding. It has
* the side effect of advancing the pointer past the
* length octets of the encoding.
*
* BUGS:
*
* This routine does not handle indefinite encodings.
* i.e. A length octet of 0x80 causes SIZE_T_MAX to
* be returned. This is the same value that is
* returned when the length octets encode a length
* equal to the highest value for a size_t.
*
* This function does not handle the definite form for
* values greater than SIZE_T_MAX. In this case it
* returns SIZE_T_MAX-1. The same value is returned
* when valid. This bug is unimportant with integers
* of 16 or more bits and data that is not expected
* to get longer than a couple kilobytes.
*
*/
{
size_t length;
short n;
length = 0;
if (BIT_8 == (**encoding)) /* indefinite form? */
length = SIZE_T_MAX;
else if (**encoding & BIT_8) { /* long form? */
n = (short) (**encoding & BITS_7654321);
if (sizeof length < n)
length = SIZE_T_MAX - 1; /* We can only
* take so
* much. */
else {
(*encoding)++;
for (; n; n--) {
length << = 8;
length |= (unsigned char) **encoding;
(*encoding)++;
}
}
} else { /* short form */
length = (unsigned char) **encoding;
(*encoding)++;
}
return length;
}
static int
isprintable(c)
int c;
/*
* This is different from the standard C function
* isprint. This only allows characters that can be
* in an ASN.1 PrintableString (universal type 19.)
*/
{
if (isalpha(c))
return TRUE;
if (isdigit(c))
return TRUE;
if (NULL != strrchr(" '()+,-./:=?", c))
return TRUE;
return FALSE;
}
static void
show_string(string, length)
char *string;
size_t length;
{
size_t i;
unsigned char c;
putchar('\"');
for (i = 0; i < length; i++) {
c = string[i];
if (isprint(c) && (127 > c)) {
switch (c) {
case '\\':
case '\"':
putchar('\\');
default:
putchar(c);
}
} else if (iscntrl(c)) {
putchar('\\');
switch (c) {
case '\a':
putchar('a');
break;
case '\b':
putchar('b');
break;
case '\f':
putchar('f');
break;
case '\n':
putchar('n');
break;
case '\r':
putchar('r');
break;
case '\t':
putchar('t');
break;
case '\v':
putchar('v');
break;
default:
printf("x%02hX", c);
}
} else
printf("\\x%02hX", c);
}
putchar('\"');
}
static void
show_hex_string(string, length)
char *string;
size_t length;
{
size_t i;
putchar('\'');
for (i = 0; i < length; i++) {
printf("%02hX", string[i]);
}
fputs("\'H", stdout);
}
static void
show_octet_string(string, length)
char *string;
size_t length;
{
size_t i;
unsigned char c;
for (i = 0; i < length; i++) {
if (!isprintable(string[i]))
break;
}
if (i < length)
show_hex_string(string, length);
else
show_string(string, length);
}
static void
show_bit_string(string, length)
char *string;
size_t length;
{
size_t i;
unsigned char c;
unsigned char mask;
unsigned char first_unused_bit;
if (1 > length)
punt("Bitstring length must be > 0.");
if (1 == length && 0 != *string)
punt("An empty bitstring must have no \
unused bits.");
if (7 < *string)
punt("can't have more than 7 unused bits \
in last octet.");
putchar('\'');
length--;
for (i = 1; i < length; i++) {
c = string[i];
for (mask = BIT_8; mask; mask >> = 1)
putchar(mask & c ? '1' : '0');
}
if (0 < length) {
first_unused_bit = (unsigned char)
(1 << *string >> 1);
for (mask = BIT_8;
mask ^ first_unused_bit;
mask >> = 1)
putchar(mask & string[length] ? '1' : '0');
}
fputs("\'B", stdout);
}
static void
show_boolean(string, length)
char *string;
size_t length;
{
if (0 == *string)
printf("FALSE");
else
printf("TRUE");
}
static void
show_integer(string, length)
char *string;
size_t length;
{
signed char *c;
signed char *end;
long x;
x = 0;
end = string + length;
for (c = string; c < end; c++) {
x << = 8;
x |= *c;
}
printf("%ld", x);
}
char *
show_value_notation(encoding, length, level)
char *encoding;
size_t length;
unsigned int level;
/*
* This function converts from transfer syntax to
* value notation. It returns a pointer to the octet
* past the encoding. It calls itself to decode
* constructed encodings.
*/
{
char *next_octet;
int i;
int asn1_identifier;
size_t asn1_length;
char *asn1_contents;
unsigned short id_class;
unsigned short id_complexity;
unsigned short id_tag_number;
static char *class[] =
{"UNIVERSAL ", "APPLICATION ", "", "PRIVATE "};
if (99 < level)
punt("We're in too deep.");
next_octet = encoding + length;
asn1_identifier = (unsigned char) *encoding;
id_class = the_class(encoding);
id_complexity = the_complexity(encoding);
id_tag_number = the_tag_number(&encod